package com.v9.common.jwt;

import com.v9.common.dto.UserDto;
import com.v9.common.model.TokenEnum;
import com.v9.common.utils.StringUtils;
import io.jsonwebtoken.*;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.security.Key;
import java.text.MessageFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @program: spring-security
 * @description:
 * @author: fbl
 * @create: 2020-12-02 14:08
 **/
@Component
public class JwtTokenUtils {
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    private static String TOKEN_USER = "token:user:{0}";

    /**
     * 创建token
     */
    public String createToken(UserDto user) {

        byte[] keyBytes = Decoders.BASE64.decode(TokenEnum.SECRET.getValue());
        Key key = Keys.hmacShaKeyFor(keyBytes);

        HashMap<String, Object> map = new HashMap<>();
        // 转换成字符串
//        String permissionStr = String.join(",", user.getPermissions());
////        map.put(TokenEnum.ROLE_CLAIMS.getValue(), permissionStr);
        map.put(TokenEnum.PIN.getValue(), user.getPin());
        String uuid = StringUtils.getUUID();
        map.put(TokenEnum.TOKEN.getValue(), uuid);

        String token = Jwts.builder()
                .signWith(key, SignatureAlgorithm.HS512)
                // 这里要早set一点，放到后面会覆盖别的字段
                .setClaims(map)
                .setIssuer(TokenEnum.ISS.getValue())
                .setSubject(user.getUsername())

                .setIssuedAt(new Date())
                // 过期时间由redis控制，jwt不再设置过期时间
                //.setExpiration(new Date(System.currentTimeMillis() + TokenEnum.EXPIRATION.getTime() * 1000))
                .compact();

        // 置入redis
        user.setToken(token);
        user.setLoginTime(System.currentTimeMillis());
        // 当前时间加上过期时间（一个小时）的毫秒数
        Long expireTime = user.getLoginTime() + (long) TokenEnum.EXPIRATION.getTime() * TokenEnum.MILLIS_SECOND.getTime();
        user.setExpireTime(expireTime);
        refreshToken(user);
        return token;
    }


    /**
     * 刷新令牌有效期
     *
     * @param user 登录信息
     */
    public void refreshToken(UserDto user) {
        // redis超时时间
        Integer timeout = TokenEnum.EXPIRATION.getTime();
        // 置入redis
        redisTemplate.opsForValue().set(user.getToken(), user, timeout, TimeUnit.SECONDS);
        String format = MessageFormat.format(TOKEN_USER, user.getPin());
        stringRedisTemplate.opsForValue().set(format, user.getToken(), timeout, TimeUnit.SECONDS);
    }

    /**
     * 验证令牌有效期，相差不足20分钟，自动刷新缓存
     *
     * @param dto
     * @return 令牌
     */
    public void verifyToken(UserDto dto) {
        long expireTime = dto.getExpireTime();
        long currentTime = System.currentTimeMillis();
        if (expireTime - currentTime <= TokenEnum.MILLIS_MINUTE_TEN.getTime()) {
            refreshToken(dto);
        }
    }


    public UserDto getUser(String token) {
        if(StringUtils.isBlank(token)){
            return null;
        }
        UserDto redisUserDto = (UserDto) redisTemplate.opsForValue().get(token);
        if (Objects.nonNull(redisUserDto)) {
            return redisUserDto;
        }
        return null;
    }

    /**
     * 删除用户身份信息
     */
    public void delLoginUser(String token) {
        if (StringUtils.isNotEmpty(token)) {
            redisTemplate.delete(token);
        }
    }

    /**
     * 删除用户身份信息
     */
    public void delLoginUserByPin(String pin) {
        String pinMapToken = MessageFormat.format(TOKEN_USER, pin);
        String token = stringRedisTemplate.opsForValue().get(pinMapToken);
        if (StringUtils.isNotEmpty(token)) {
            redisTemplate.delete(List.of(pinMapToken, token));
        }
    }

    /**
     * 是否已过期
     */
    public boolean isExpiration(String token) {
        try {
            return getTokenBody(token).getExpiration().before(new Date());
        } catch (ExpiredJwtException e) {
            return e.getClaims().getExpiration().before(new Date());
        }
    }

    /**
     * 获取token
     *
     * @param token
     * @return
     */
    private Claims getTokenBody(String token) throws SignatureException {
        return Jwts.parser()
                .setSigningKey(TokenEnum.SECRET.getValue())
                .parseClaimsJws(token)
                .getBody();
    }



    /**
     * 获取用户id
     *
     * @param token
     * @return
     */
    public String getPin(String token) {
        try {
            return (String) getTokenBody(token).get(TokenEnum.PIN.getValue());
        } catch (ExpiredJwtException e) {
            return (String) e.getClaims().get(TokenEnum.PIN.getValue());
        }

    }

}

